package com.wkbb.sports.controller;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

import com.wkbb.common.handler.JsonUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.github.pagehelper.PageInfo;
//import com.wkbb.basic.thread.ImportStudentTask;
import com.wkbb.common.base.BaseController;
import com.wkbb.common.constants.ErrorCode;
import com.wkbb.common.constants.RedisKeyConstant;
import com.wkbb.common.constants.VersionConstant;
import com.wkbb.common.dto.ResultDTO;
import com.wkbb.common.dto.basic.OrganizationDto;
import com.wkbb.common.dto.basic.UserDto;
import com.wkbb.common.redis.RedisTemplateUtil;
import com.wkbb.common.utils.ExcelUtil;
import com.wkbb.common.utils.ListUtil;
import com.wkbb.common.utils.ThreadPoolUtil;
import com.wkbb.sports.dto.PhysicalDto;
import com.wkbb.sports.feign.BasicFeignService;
import com.wkbb.sports.service.PhysicalService;
import com.wkbb.sports.thread.ImportPhysicalTask;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;

/**
 * @author dengjy
 * 创建时间：2021-01-20 15:37
 */
@RestController
@Api(description = "体质项目管理")
@RequestMapping("/sports/{apiVersion}/physical")
@Slf4j
public class PhysicalController extends BaseController{

    @Autowired
    private PhysicalService physicalService;
    @Autowired
    private BasicFeignService basicFeignService;

    @Autowired
    private RedisTemplateUtil redisTemplateUtil;

    private static final Integer PAGE_SIZE = 20;
    private static final Integer MAX_IMPORT_NUM_OF_CLEAR_ADDRESS_BOOK = 50;//一次性导入学生数量超过这个值，就不再单个清理通讯录缓存

    @ApiOperation(value = "查询体质项目分页列表")
    @RequestMapping(value = "getPhysicalPageList", method = RequestMethod.GET)
    public ResultDTO<PageInfo<PhysicalDto>> getPhysicalPageList(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @ApiParam(value = "组织ID", required = true) @RequestParam(name = "orgId", required = true) Long orgId,
            @ApiParam(value = "年级id", required = false) @RequestParam(name = "gradeId", required = false) Long gradeId,
            @ApiParam(value = "页码", required = true, defaultValue = "1") @RequestParam(name = "pageNo", required = true, defaultValue = "1") Integer pageNo,
            @ApiParam(value = "每页条数", required = true, defaultValue = "10") @RequestParam(name = "pageSize", required = true, defaultValue = "10") Integer pageSize)
    {
        log.info("getPhysicalPageList orgId:{} gradeId{} pageNo:{} pageSize:{}",orgId,gradeId,pageNo,pageSize);
        try {
            if (orgId == null || orgId.equals("")) {
                return new ResultDTO<>(ErrorCode.PARAM_MISSING);
            }
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("orgId", orgId);
            params.put("gradeId",gradeId);
            return new ResultDTO<>(physicalService.getPhysicalPageList(params,pageNo,pageSize));
        } catch (Exception e) {
            log.error("***********PhysicalController.getPhysicalPageList******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }

    @ApiOperation(value = "新增体质项目")
    @RequestMapping(value = "/addPhysical", method = RequestMethod.POST)
    public ResultDTO addPhysical(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @RequestBody PhysicalDto physicalDto) {
        log.info("addPhysical {}",physicalDto.toString());
        try {
            if (physicalDto.getOrgId() ==null){
                return new ResultDTO(ErrorCode.PARAM_MISSING);
            }
            return physicalService.addPhysical(physicalDto);
        } catch (Exception e) {
            log.error("***********PhysicalController.addPhysical******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }

    @ApiOperation(value = "修改体质项目")
    @RequestMapping(value = "/updatePhysical", method = RequestMethod.PUT)
    public ResultDTO updatePhysical(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @RequestBody PhysicalDto physicalDto) {
        log.info("updatePhysical:{}",physicalDto.toString());
        try {
            if (physicalDto.getOrgId() ==null || physicalDto.getId() == null){
                return new ResultDTO(ErrorCode.PARAM_MISSING);
            }
            return physicalService.updatePhysical(physicalDto);
        }catch (Exception e){
            log.error("***********PhysicalController.updatePhysical******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }

    @ApiOperation(value = "体质项目详情")
    @RequestMapping(value = "/getPhysical/{physicalId}", method = RequestMethod.GET)
    public ResultDTO getPhysical(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @ApiParam(value = "体检项目id", required = true) @PathVariable(value = "physicalId", required = true) String physicalId,
            @ApiParam(value = "组织ID", required = true) @RequestParam(name = "orgId", required = true) Long orgId
    ){
        log.info("getPhysical orgId:{}",physicalId,orgId);
        try {
            if (orgId == null || physicalId == null ){
                return new ResultDTO(ErrorCode.PARAM_MISSING);
            }
            Map<String,Object> params = new HashMap<>();
            params.put("orgId",orgId);
            params.put("physicalId",physicalId);

            return physicalService.getPhysicalById(params);
        }catch (Exception e){
            log.error("***********PhysicalController.getPhysical******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }

    @ApiOperation(value = "删除体质项目")
    @RequestMapping(value = "/delPhysical", method = RequestMethod.DELETE)
    public ResultDTO delPhysical(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @ApiParam(value = "组织ID", required = true) @RequestParam(name = "orgId", required = true) Long orgId,
            @ApiParam(value = "体质id", required = true) @RequestParam(name = "physicalId", required = true) Long physicalId) {
        log.info("delPhysical orgId:{} physicalId:{}", orgId, physicalId);
        try {
            if (orgId == null || orgId.equals("") || physicalId == null || physicalId.equals("")){
                return new ResultDTO(ErrorCode.PARAM_MISSING);
            }
            Map<String,Object> params = new HashMap<>();
            params.put("orgId",orgId);
            params.put("physicalId",physicalId);
            return physicalService.delPhysical(params);
        }catch (Exception e){
            log.error("***********PhysicalController.delPhysical******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }

    @ApiOperation(value = "启用禁用体质项目")
    @RequestMapping(value = "/updatePhysicalState", method = RequestMethod.PUT)
    public ResultDTO updatePhysicalState(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @ApiParam(value = "体质id", required = true) @RequestParam(name = "physicalId", required = true) Long physicalId,
            @ApiParam(value = "状态(0禁用 1启用)", required = true) @RequestParam(name = "state", required = true) Integer state) {
        log.info("updatePhysicalState physicalId:{} state:{}", physicalId, state);
        try {
            if (physicalId == null || state == null){
                return new ResultDTO(ErrorCode.PARAM_MISSING);
            }

            return physicalService.updatePhysicalState(physicalId,state);
        }catch (Exception e){
            log.error("***********updatePhysicalState******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }

    @ApiOperation(value = "复制学校体质项目")
    @RequestMapping(value = "/initPhysical", method = RequestMethod.POST)
    public ResultDTO initPhysical(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
            @ApiParam(value = "年级类型(1幼儿园 2小学 3中职 4初中 5高中 6高职 7本科 8完中 9九年一贯 10十二年一贯)", required = true) @RequestParam(name = "gradeType", required = true) Integer gradeType,
            @ApiParam(value = "项目ID", required = false) @RequestParam(name = "projectId", required = false) Long projectId,
            @ApiParam(value = "原学校ID", required = true) @RequestParam(name = "orignOrgId", required = true) Long orignOrgId,
            @ApiParam(value = "复制组织ID", required = true) @RequestParam(name = "targetOrgId", required = true) Long targetOrgId)
    {
        log.info("initPhysical projectId:{} orignOrgId:{} targetOrgId:{}",projectId,orignOrgId,targetOrgId);
        try {
            if (orignOrgId == null || null==targetOrgId){
                return new ResultDTO(ErrorCode.PARAM_MISSING);
            }

            ResultDTO<OrganizationDto> organizationResult = basicFeignService.getOrgById(targetOrgId);
            if(organizationResult.isSuccess() && null!=organizationResult.getData()) {
                physicalService.copyRule(projectId, orignOrgId, targetOrgId);
            }
            return new ResultDTO(true);
        }catch (Exception e){
            log.error("***********PhysicalController.initPhysical******", e);
            return new ResultDTO<>(ErrorCode.SYSTEM_EXCEPTION);
        }
    }
    
    
    
    @ApiOperation(value = "导入标准")
    @PostMapping(value = "importExcelFile")
    public ResultDTO importExcelFile(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion", required = true) String apiVersion,
//            @ApiParam(value = "学校ID", required = false, defaultValue = "1") @RequestParam(name = "orgId", required = false) Long orgId,
            @RequestParam("uploadFile") MultipartFile uploadFile) {
    	
        UserDto userDto = getUserByLogin();
        Long orgId = 1L;

        if (userDto == null) {
            return new ResultDTO(ErrorCode.USER_NOT_EXIST);
        }

      
        if (uploadFile.isEmpty()) {
            log.error("---StudentManageController.importExcelFile---文件不存在！");
            return new ResultDTO(ErrorCode.BUSINESS_DATA_WRONG);
        }
        
        Integer columns = 0;

        String redisKey = RedisKeyConstant.WKBB_SYSTEM + "importExcelFile:" + "orgId:" + orgId;
        String importExcelFileRedisKey = RedisKeyConstant.WKBB_SYSTEM + "importExcelFile:" +"redis:"+"orgId:" + orgId;
        String fileNames = uploadFile.getOriginalFilename();
        String type = fileNames.substring(fileNames.lastIndexOf(".") + 1);
        boolean lock = false;
        int retryCount = 0;
        while (!lock) {
            try {
                lock = redisTemplateUtil.tryLock(redisKey, userDto.getId().toString(), 15, TimeUnit.SECONDS);
                log.info("#################" + redisKey, "加锁 ", userDto.getId());

            } catch (Exception e) {
                log.info("#################加锁失败..等待锁过期 ：" + redisKey, userDto.getId());
                e.printStackTrace();
            }

            if (lock) {
                InputStream in = null;
                try {
                    //防重复数据提交并发判断
                    if (null != redisTemplateUtil.get(importExcelFileRedisKey)) {
                        redisTemplateUtil.releaseLock(redisKey, userDto.getId().toString());
                        return new ResultDTO<>(ErrorCode.STANDARD_IMPORT_EXCELL);
                    } else {
                        //设置防重复用户redis
//                        redisTemplateUtil.set(importExcelFileRedisKey, true, RedisKeyConstant.EXPIRATION_TIME_5);
                        redisTemplateUtil.set(importExcelFileRedisKey, true, RedisKeyConstant.EXPIRATION_SECOND_10);
                    }

                    in = uploadFile.getInputStream();
                    Workbook  wb;
                    
                    if ("xls".equals(type)) {
                        wb = new HSSFWorkbook(in);
                    } else {
                        wb = new XSSFWorkbook(in);
                    }
                    
                    
                    
                    List<Object[]>  projData =ExcelUtil.dataExcel(wb.getSheetAt(0));
                    List<Object[]>  ruleData =ExcelUtil.dataExcel(wb.getSheetAt(1));
                    List<Object[]> 	ruleDetailData = ExcelUtil.dataExcel(wb.getSheetAt(2));
                    List<Object[]> 	rulePlusData = ExcelUtil.dataExcel(wb.getSheetAt(3));
                    
                    Map<String,Object> map = new HashMap<>();
                    
                    
                    if (!CollectionUtils.isEmpty(projData) && !CollectionUtils.isEmpty(ruleData) && !CollectionUtils.isEmpty(ruleDetailData)) {
                    	map.put("projData", projData);
                    	map.put("ruleData", ruleData);
                    	map.put("ruleDetailData", ruleDetailData);
                    	map.put("rulePlusData", rulePlusData);

                        //被强制取消，处理完所有线程后，再返回给用户界面提示异常
                        return preSaveOfImport(userDto, orgId, map);
                    }

                } catch (RejectedExecutionException e) {
                    log.error("----已达到线程池最大等待队列---", e);
                    redisTemplateUtil.releaseLock(redisKey, userDto.getId().toString());
                    redisTemplateUtil.delete(importExcelFileRedisKey);
                    return new ResultDTO(ErrorCode.SYSTEM_THREAD_MAXQUEUE);
                }catch (ArrayIndexOutOfBoundsException e){
                    log.error("----导入模板异常,请检测---", e);
                    redisTemplateUtil.releaseLock(redisKey, userDto.getId().toString());
                    redisTemplateUtil.delete(importExcelFileRedisKey);
                    return new ResultDTO(ErrorCode.IMPORT_EXCEL_ERROR);
                }catch (Exception e) {
                    redisTemplateUtil.releaseLock(redisKey, userDto.getId().toString());
                    redisTemplateUtil.delete(importExcelFileRedisKey);
                    log.error("----StudentManageController.importExcelFile----", e);
                    return new ResultDTO(ErrorCode.BUSINESS_DATA_WRONG);
                } finally {
                    if (null != in) {
                        try {
                            in.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    try {
                        log.info("#################" + redisKey, "开始释放锁 ", userDto.getId());
                        redisTemplateUtil.releaseLock(redisKey, userDto.getId().toString());
                        log.info("#################" + redisKey, "释放锁成功 ", userDto.getId());
                        redisTemplateUtil.delete(importExcelFileRedisKey);
                    } catch (Exception e) {
                        log.error("#################释放锁失败..等待锁过期 ," + redisKey, "释放锁成功 ", userDto.getId().toString());
                        e.printStackTrace();
                    }
                }
            } else {
                try {
                    retryCount++;
                    if(retryCount>20){
                        log.info("加锁失败，重试超过20次，导入标准失败...orgId:{}" ,  orgId);
                        break;
                    }
                    log.info("等待500ms重试..." + redisKey, "释放锁成功 ", userDto.getId().toString());
                    Thread.sleep(500L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return new ResultDTO(ErrorCode.BUSINESS_DATA_WRONG);
    }
    
    
    
    
    private ResultDTO<Map<String,Object>> preSaveOfImport(UserDto userDto, Long orgId, Map<String,Object> map) throws Exception {
    	 List<Object[]> projData =(List<Object[]>)map.get("projData");
//         log.info("preSaveOfImport:{}", JsonUtil.objectToJson(projData));
         
        projData = projData.subList(2,projData.size());
        projData.get(0);
    	Integer total = projData.size();
        Integer pageSize = PAGE_SIZE;
        Integer pages = (total + pageSize - 1) / pageSize;

        //缓存异步请求对象
        List<FutureTask<ResultDTO<Map<String, Object>>>> futureTaskList = new ArrayList<>();

        //获取线程池
        //ExecutorService executorService = BasicThreadPoolUtil.getExecutorService(new BasicThreadPoolUtil(serviceConfig));
//        ExecutorService executorService = ThreadPoolUtil.getExecutorService();


        FutureTask<ResultDTO<Map<String, Object>>> future;
        Integer currentRow = 0;

        if(projData.size()>0){
            for (int currentPage = 1; currentPage <= pages; currentPage++) {
                List<Object[]> dataList = ListUtil.pager(currentPage, pageSize, projData);
                currentRow = (currentPage - 1) * pageSize + 4;

//                test
                ResultDTO<Map<String,Object>> arrayResultDto = physicalService.saveData(userDto,orgId,dataList,map,currentRow);
                return arrayResultDto;
//                pro
//                future = new FutureTask<ResultDTO<Map<String, Object>>>(new ImportPhysicalTask(physicalService, map, userDto, orgId, dataList,currentRow));
//                executorService.execute(future);
//                //缓存到集合中集中获取
//                futureTaskList.add(future);
            }
        }
        return new ResultDTO(ErrorCode.BUSINESS_DATA_WRONG);
    }
}
